package com.youshang520i.ratelimiter.config;

import com.youshang520i.ratelimiter.config.annotation.EnableRateLimiter;
import com.youshang520i.ratelimiter.config.annotation.RateLimiter;
import com.youshang520i.ratelimiter.config.cglib.CglibFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;

import static java.lang.Class.forName;

/**
 * @author youshang
 *
 */

@Component
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware, ResourceLoaderAware{// ,EnvironmentAware


    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    private MetadataReaderFactory metadataReaderFactory;
    private ResourcePatternResolver resourcePatternResolver;
    private ApplicationContext applicationContext;
    private Environment environment;


//    @Override
//    public void setEnvironment(Environment environment) {
//        this.environment = environment;
//    }

    /**
     * 注册Bean
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String scanPath = "";

        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            try {
                String beanClassName = registry.getBeanDefinition(beanDefinitionName).getBeanClassName();
                if (null != beanClassName){
                    Class clazz = forName(beanClassName, false, ServiceBeanDefinitionRegistry.class.getClassLoader());
                    if (clazz.isAnnotationPresent(EnableRateLimiter.class)) {
                        EnableRateLimiter annotation = (EnableRateLimiter) clazz.getAnnotation(EnableRateLimiter.class);
                        scanPath = annotation.scanPackage();
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        if (StringUtils.isEmpty(scanPath)){
            return;
        }

        //这里一般我们是通过反射获取需要代理的接口的clazz列表
        //比如判断包下面的类，或者通过某注解标注的类等等
        Set<Class<?>> beanClazzs = scannerPackages(scanPath);
        for (Class beanClazz : beanClazzs) {

            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();

            //在这里，我们可以给该对象的属性注入对应的实例。
            //比如mybatis，就在这里注入了dataSource和sqlSessionFactory，
            // 注意，如果采用definition.getPropertyValues()方式的话，
            // 类似definition.getPropertyValues().add("interfaceType", beanClazz);
            // 则要求在FactoryBean（本应用中即ServiceFactory）提供setter方法，否则会注入失败
            // 如果采用definition.getConstructorArgumentValues()，
            // 则FactoryBean中需要提供包含该属性的构造方法，否则会注入失败
            definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);

            Method[] methods = beanClazz.getMethods();
            for (Method method : methods) {
                boolean annotationPresent = method.isAnnotationPresent(RateLimiter.class);
                if (annotationPresent){

                    definition.setBeanClass(CglibFactory.class);
                    //这里采用的是byType方式注入，类似的还有byName等
                    definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);

//                    System.out.println(beanClazz.getSimpleName());  //使用JDK的代理的时候使用 beanClazz.getSimpleName()
//                    System.out.println(ClassUtils.getShortNameAsProperty(beanClazz)); // 使用Cglib的时候使用 ClassUtils.getShortNameAsProperty(beanClazz)
//                    注意
//                    1. 使用Cglib的以后注册bean需要首字母小写
//                    2. 使用JDK 代理的时候需要首字母大写
                    registry.registerBeanDefinition(ClassUtils.getShortNameAsProperty(beanClazz), definition);
                }
            }
        }
    }


    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    /**
     * 根据包路径获取包及子包下的所有类
     * @param basePackage basePackage
     * @return Set<Class<?>> Set<Class<?>>
     */
    private Set<Class<?>> scannerPackages(String basePackage) {
        Set<Class<?>> set = new LinkedHashSet<>();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + DEFAULT_RESOURCE_PATTERN;
        try {
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    Class<?> clazz;
                    try {
                        clazz = forName(className);
                        //过滤掉接口
                        if (!clazz.isInterface()){
                            set.add(clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return set;
    }

    /**
     * 将原本的包名路径切换为 斜杠路径  com.service.impl 转换后 com/service/impl
     * @param basePackage
     * @return
     */
    protected String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(basePackage));
    }

    /**
     * 获取application中的 获取当前上下问的环境
     * @return
     */
    private Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }

    /**
     *根据标准修改应用程序上下文的内部bean工厂
     * *初始化。所有bean定义都已加载，但没有bean
     * *还没有实例化。这允许重写或添加
     * *属性，甚至急于初始化bean。
     * 应用程序上下文使用的bean工厂
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

//    @Override
//    public void setEnvironment(Environment environment) {
//        this.environment = environment;
//    }
}
